#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>


#define BUF_SIZE 1024


void new_file( char buf[], size_t buf_size)
{
	srandom( getpid() );
	
	char tmp_buf[256];
	int fd;
	do
	{
		int i;
		for ( i = 0; i < buf_size-1; i++)
		{
			char tmp = 'a' + (char)( ( (float)random() / RAND_MAX ) * 26);
			tmp_buf[ i] = tmp;
		}
		tmp_buf[ buf_size-1] = '\0';

		
		strcpy( buf, "z");
		strcat( buf, tmp_buf);
		buf[ buf_size] = '\0';
		fd = open( buf, O_CREAT | O_EXCL , 0644);
	}while( ( fd == -1) && ( errno == EEXIST) );
	if ( errno != EEXIST)
	{
		perror("new_file");
		exit(0);
	}
	close( fd);
}

int main( int argc, char **argv)
{
	if ( argc != 2)
	{
		printf("Wrong arguments\n");
		exit(0);
	}
	
	int fd_in = open( (char*)argv[1], O_RDONLY);
	if ( fd_in == -1)
	{
		printf("Can't open file: %s",(char*)argv[1]);
		exit(0);
	}
///////////////////////////////////////////////
	open( ".for_keys", O_CREAT | O_EXCL , 0644);
	
	key_t key_sem_common = ftok( ".for_keys", 1);
	key_t key_shm_common = ftok( ".for_keys", 2);
///////////
	char file_name[9];
	new_file( file_name,8);
	
	key_t key_sem  = ftok( file_name,1);
	
	key_t key_shm_buffer   = ftok( file_name,2);
	key_t key_shm_count    = ftok( file_name,3);
///////////
	int sem_id_common = semget( key_sem_common ,2,IPC_CREAT | 0644);
	int sem_id        = semget( key_sem        ,3,IPC_CREAT | 0644);	
///////////////
	int shm_id_common = shmget( key_shm_common, 9*sizeof( char), IPC_CREAT | 0644);

	int shm_id_buffer = shmget( key_shm_buffer , BUF_SIZE      , IPC_CREAT | 0644);
	int shm_id_count  = shmget( key_shm_count  , sizeof(int)   , IPC_CREAT | 0644);
///////////////	
	int  *ptr_shm_common = (int* ) shmat( shm_id_common, NULL, NULL);
	char *ptr_shm_buffer = (char*) shmat( shm_id_buffer, NULL, NULL);
	int  *ptr_shm_count  = (int* ) shmat( shm_id_count , NULL, NULL);
	
///////////////////////////////////////////////	
	struct sembuf sem_buf[ 10];
	struct sembuf sem_full_inc;
		sem_full_inc.sem_num =  1;    sem_full_inc.sem_op  =  2;   sem_full_inc.sem_flg = NULL;
	
	struct sembuf sem_full_dec;
		sem_full_dec.sem_num = 1;    sem_full_dec.sem_op = -2;   sem_full_dec.sem_flg = NULL;
	
	struct sembuf sem_empty_inc;
		sem_empty_inc.sem_num =  0;    sem_empty_inc.sem_op  =  2;   sem_empty_inc.sem_flg = NULL;
		
	struct sembuf sem_empty_dec;
		sem_empty_dec.sem_num = 0;    sem_empty_dec.sem_op = -2;  sem_empty_dec.sem_flg = SEM_UNDO;
////////////////////////////////////////////////
	semop( sem_id_common, &sem_empty_dec,1);
	
			strcpy( ptr_shm_common, file_name);
	
	semop ( sem_id_common, &sem_full_inc,1);
////////////////////////////////////////////////
	sem_buf[0].sem_num = 0;    sem_buf[0].sem_op =  2;   sem_buf[0].sem_flg = SEM_UNDO;
	sem_buf[1].sem_num = 0;    sem_buf[1].sem_op = -2;   sem_buf[1].sem_flg = NULL;
	
	semop( sem_id_common, sem_buf, 2);
////////////////////////////////////////////////
		sem_buf[0].sem_num = 1;    sem_buf[0].sem_op =  3;   sem_buf[0].sem_flg = NULL;
		sem_buf[1].sem_num = 1;    sem_buf[1].sem_op = -3;   sem_buf[1].sem_flg = SEM_UNDO;
		
	semop( sem_id, sem_buf, 2);
		
	int read_count;	
	do
	{
		semop( sem_id, &sem_empty_dec,1);
			if ( semctl( sem_id, 0 , GETVAL) != 0){
				printf("Writer is killed\n");
				exit(0);
			}
			
			read_count = read( fd_in, ptr_shm_buffer, BUF_SIZE);
			if ( read_count == -1)
			{
				perror("In read error");
				exit(0);
			}
			else
			{
				*ptr_shm_count = read_count;		
			}

		semop( sem_id , &sem_full_inc, 1);
	}
	while( read_count);
/////////////////
	sem_buf[0].sem_num = 0;    sem_buf[0].sem_op =  -3;   sem_buf[0].sem_flg = NULL;
	
	semop( sem_id, sem_buf, 1);
////////
	shmdt( ptr_shm_common);
	shmdt( ptr_shm_buffer);
	shmdt( ptr_shm_count);
	
	shmctl( shm_id_buffer, IPC_RMID , NULL);
	shmctl( shm_id_count,  IPC_RMID , NULL);
	
	semctl( sem_id, 0, IPC_RMID, 0);
	
	unlink( file_name);
}